package me.timlong.service.impl;

import me.timlong.dao.OrderDOMapper;
import me.timlong.dao.SequenceDOMapper;
import me.timlong.dao.StockLogDOMapper;
import me.timlong.dataobject.OrderDO;
import me.timlong.dataobject.SequenceDO;
import me.timlong.dataobject.StockLogDO;
import me.timlong.error.BusinessException;
import me.timlong.error.EmBusinessError;
import me.timlong.service.ItemService;
import me.timlong.service.OrderService;
import me.timlong.service.UserService;
import me.timlong.service.model.ItemModel;
import me.timlong.service.model.OrderModel;
import me.timlong.service.model.UserModel;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ItemService itemService;

    @Autowired
    private UserService userService;

    @Autowired
    private OrderDOMapper orderDOMapper;

    @Autowired
    private SequenceDOMapper sequenceDOMapper;

    @Autowired
    private StockLogDOMapper stockLogDOMapper;

    @Override
    @Transactional
    public OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount, String stockLogId) throws BusinessException {
        // 1. 校验下单状态，下单的商品是否存在， 用户是否存在，购买数量是否合法， 校验活动信息
        // ItemModel itemModel = itemService.getItemById(itemId);
        ItemModel itemModel = itemService.getItemByIdInCache(itemId);
        if (null == itemModel) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "商品信息不存在");
        }

        UserModel userModel = userService.getUserByIdInCache(userId);
        if (null == userModel)
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "用户信息不存在");

        if (amount <= 0 || amount > 99)
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "购物数量不正确");

        if (null != promoId) {
            // 1、校验该活动是否适用于该商品
            if (promoId.intValue() != itemModel.getPromoModel().getId()) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动信息不正确");
                // 2、校验该活动是否正在进行中
            } else if (2 != itemModel.getPromoModel().getStatus()) {
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "活动还未开始");
            }
        }

        // 2. 落单减库存  or 付款减库存
        boolean res = itemService.decreaseStock(itemId, amount);
        if (!res) {
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        }

        // 3. 订单入库
        OrderModel orderModel = new OrderModel();
        orderModel.setItemId(itemId);
        orderModel.setAmount(amount);
        orderModel.setUserId(userId);
        if (null != promoId) {
            orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
        } else {
            orderModel.setItemPrice(itemModel.getPrice());
        }
        orderModel.setPromoId(promoId);
        orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));

        // 生成交易流水号，订单号
        orderModel.setId(generateOrderNo());
        OrderDO orderDO = convertFromOrderModel(orderModel);

        orderDOMapper.insertSelective(orderDO);

        // 增加商品的销量
        itemService.increaseSales(itemId, amount);

        // 设置库存流水为成功
        StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);
        if(null == stockLogDO) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        }
        stockLogDO.setStatus(2);
        stockLogDOMapper.updateByPrimaryKeySelective(stockLogDO);




//        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
//            @Override
//            public void afterCommit() {
//                // 异步更新库存
//                // 这里的设计在于对异步更新库存的消息在会后发送更新，当代大事务中的所有的工作完成后在异步同步数据库信息，
//                // 如果异步更新失败，则回滚redis缓存的库存扣减，从而避免落单失败导致的数据库信息与真实数据不一致的情况
//                boolean mqResult = itemService.asyncDecreaseStock(itemId, amount);
//                //  if(!mqResult) {
//                //      itemService.increaseStock(itemId, amount);
//                //      throw new BusinessException(EmBusinessError.MQ_SEND_FAIL);
//                //  }
//            }
//        });

        // 4. 返回前端
        return orderModel;
    }

    // 为了保证全局一致性，当在createOrder创建订单方法中执行失败，该方法也不应该被回滚
    // 因此这里设置为requres_new, 保证及时调用者事务执行失败也不会回滚sequence_info表的操作
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    String generateOrderNo() {
        // 订单号设计为16位
        // 前八位设置为日期，年月日
        StringBuilder stringBuilder = new StringBuilder();
        LocalDateTime now = LocalDateTime.now();
        String nowDate = now.format(DateTimeFormatter.ISO_DATE).replace("-", "");
        stringBuilder.append(nowDate);

        // 中间六位自增序列, 这里是通过数据库中的sequence_info表中的数据查询和更新获得的递增序列
        // 数据库总的sequence_info表是专门为获得递增序列设计的

        SequenceDO sequenceDO = sequenceDOMapper.getSequenceByName("order_info");
        int sequence = sequenceDO.getCurrentValue();
        sequenceDO.setCurrentValue(sequenceDO.getCurrentValue() + sequenceDO.getStep());
        sequenceDOMapper.updateByPrimaryKeySelective(sequenceDO);

        // 用sequence填充6位，不足的补零
        String sequenceStr = String.valueOf(sequence);
        for (int i = 0; i < 6 - sequenceStr.length(); i++) {
            stringBuilder.append(0);
        }
        stringBuilder.append(sequenceStr);

        // 最后两位分库分表位，暂时写死为“00”
        stringBuilder.append("00");

        return stringBuilder.toString();
    }

    private OrderDO convertFromOrderModel(OrderModel orderModel) {
        if (null == orderModel)
            return null;
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderModel, orderDO);
        orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
        orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());

        return orderDO;
    }
}
